﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Tomers.WPF.Shapes;
using System.Threading;
using System.IO;

namespace epk23.PartIIProject
{
    /// <summary>
    /// Interaction logic for MachineWindow.xaml
    /// </summary>
    public partial class MachineWindow : Window
    {
        private MachineGraph machineGraph;

        private Interpreter interpreter;
        private SpeedManager speedManager;

        public MachineWindow()
        {
            InitializeComponent();
            RuleNode.window = this;

            MachineGraph.window = this;
            MachineGraph.canvas = machineCanvas;
            machineGraph = new MachineGraph();
            machineGraph.refreshGraph();

            RuleNode.graph = machineGraph;

            interpreter = new Interpreter();
            interpreter.machineWindow = this;

            speedManager = new SpeedManager();

            interpreter.speedManager = speedManager;

            refreshComboMachines();
        }

        private void refreshComboMachines()
        {
            foreach (string s in Directory.GetFiles("C:\\Machines"))
            {
                String machineName = s.Remove(0, 12);
                machineName = machineName.Remove(machineName.Length - 5);
                comboMachines.Items.Add(machineName);
            }
        }

        public void changeGraph(MachineGraph newGraph)
        {
            machineGraph.nodes = newGraph.nodes;
            machineGraph.arrows = newGraph.arrows;
            machineGraph.ruleNumbers = newGraph.ruleNumbers;
        }

        private void cmdAddIncrNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addIncrRuleNode(20, 20, txtRegName.Text, machineCanvas);
            refreshRegValues();
        }

        private void cmdAddDecrNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addDecrRuleNode(150, 20, txtRegName.Text, machineCanvas);
            refreshRegValues();
        }

        private void cmdAddHaltNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addHaltRuleNode(280, 20, machineCanvas);

        }

        private void cmdAddMachineNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addMachineNode(150, 150, (string)comboMachines.SelectedItem, machineCanvas);
            refreshRegValues();
        }

        private void cmdPrintRules_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.machine.printRules();
        }

        private void cmdRun_Click(object sender, RoutedEventArgs e)
        {
            run();
        }

        private void run()
        {
            speedManager.Speed = (int)sliderSpeed.Value;

            Machine m = machineGraph.machine;

            //int[] args = new int[0];

            //if (txtArgs.Text != "")
            //{
            //String[] argStringArray = txtArgs.Text.Split(' ');
            //args = new int[argStringArray.Length];


            //    for (int i = 0; i <= argStringArray.Length - 1; i++)
            //    {
            //        args[i] = Int32.Parse(argStringArray[i]);
            //    }
            //}


            interpreter.run(m);
        }

        private void cmdStep_Click(object sender, RoutedEventArgs e)
        {
            if (interpreter.running == false)
            {
                run();
                Thread.Sleep(50);
            }

            interpreter.semStep.Release();
        }

        private void sliderSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (speedManager != null) //to prevent exceptions from event firing at initialization
            {
                speedManager.Speed = (int)sliderSpeed.Value;

                if (sliderSpeed.Value == 0)
                    cmdStep.IsEnabled = true;
                else
                {
                    cmdStep.IsEnabled = false;
                    if (speedManager.Waiting) interpreter.semStep.Release();  //If the interpreter is waiting on the step semaphore, must release it to continue execution.
                }
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            interpreter.thread.Abort();
        }

        private void cmdSave_Click(object sender, RoutedEventArgs e)
        {
            bool exists = false;
            foreach (String s in Directory.GetFiles("C:\\Machines"))
            {
                if (s.Remove(0, 12) == txtMachineName.Text + ".regm")
                {
                    exists = true;
                    break;
                }
            }
            if (!exists)
                comboMachines.Items.Add(txtMachineName.Text + ".regm");

            FileManager.writeFile(machineGraph, txtMachineName.Text);
        }

        private void txtMachineName_GotFocus(object sender, RoutedEventArgs e)
        {
            txtMachineName.Text = "";
        }

        private void cmdLoad_Click(object sender, RoutedEventArgs e)
        {
            if (comboMachines.SelectedItem != null)
            {
                clearCanvas();

                MachineGraph newGraph = MachineGraph.loadGraph((string)comboMachines.SelectedItem, machineGraph);

                machineGraph = newGraph;
                machineGraph.makeStartMarker();
                RuleNode start = (RuleNode)machineGraph.lookupNode(machineGraph.machine.startRuleNumber);
                machineGraph.setStartMarker(start);
                refreshRegValues();
            }
        }

        private void clearCanvas()
        {
            machineCanvas.Children.Clear();
        }

        private void cmdClear_Click(object sender, RoutedEventArgs e)
        {
            clearCanvas();
            MachineGraph newMachineGraph = new MachineGraph();
            machineGraph = newMachineGraph;
            RuleNode.graph = newMachineGraph;
            refreshRegValues();
        }

        private void cmdSetRegValue_Click(object sender, RoutedEventArgs e)
        {
            if (comboRegisters.SelectedItem != null)
            {
                machineGraph.machine.lookupRegister(((string)comboRegisters.SelectedItem)).value = Int32.Parse(txtRegValue.Text);
                refreshRegValues();
            }
        }

        public void refreshRegValues()
        {
            lstRegNames.Items.Clear();
            lstRegValues.Items.Clear();
            foreach (Register r in machineGraph.machine.registers)
            {
                lstRegNames.Items.Add(r.regName);
                lstRegValues.Items.Add(r.value);
            }
        }

        private void comboRegisters_DropDownOpened(object sender, EventArgs e)
        {
            comboRegisters.Items.Clear();
            foreach (Register r in machineGraph.machine.registers)
            {
                comboRegisters.Items.Add(r.regName);
            }
        }

        private void comboMachines_DropDownOpened(object sender, EventArgs e)
        {
            comboMachines.Items.Clear();
            refreshComboMachines();
        }

        private void cmdRefreshRegisters_Click(object sender, RoutedEventArgs e)
        {
            refreshRegValues();
        }

        private void cmdSetStart_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.settingStart = true;
        }

        public void executionFinished()
        {
            refreshRegValues();
        }

        private void cmdUndo_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.undo();
        }

        private void cmdRedo_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.redo();
        }
    }
}
